const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

function log(value) {
	console.log(value)
}

class MyPromise {
	constructor(executor) {
		// 判断 executor 是否是一个函数
		// 如果是一个函数就执行这个函数
		// 不是一个函数就直接报错
		if (typeof executor === "function") {
			executor(this.resolve, this.reject);
		} else {
			throw new TypeError(`Promise resolver ${executor} is not a function`);
		}
	}

	// promise 实例的三种状态
	// 状态只能从 PENDING => FULFILLED 或 PENDING => REJECTED
	// 状态只能改变一次
	status = PENDING;
	value = undefined;
	reason = undefined;

	// promise then 的成功回调和失败回调队列
	successCallback = [];
	failCallback = [];

	// resolve：promise executor异步操作成功执行的回调 将状态 PENDING 改成 FULFILLED
	// resolve参数value： 为操作成功后得到值
	// 并执行成功回调队列中的所有回调函数，并将value作为回调函数的参数
	resolve = (value) => {
		if (this.status !== PENDING) return;
		this.status = FULFILLED;
		this.value = value;
		// this.successCallback.forEach((fn) => fn(this.value));
		while (this.successCallback.length) this.successCallback.shift()()
	};

	// reject：promise executor异步操作失败执行的回调 将状态 PENDING 改成 REJECTED
	// reject参数reason： 为异步操作失败的原因
	// 并执行失败回调队列中的所有回调函数，并将失败原因作为回调函数的参数
	reject = (reason) => {
		if (this.status !== PENDING) return;
		this.reason = reason;
		this.status = REJECTED;
		while (this.failCallback.length) this.failCallback.shift()()
		//this.failCallback.forEach((fn) => fn(this.resolve));

	};

	// then: 定义promise executor异步操作成功和失败的处理函数
	// 在状态改变的时候回执行对应的定义函数
	// then 会返回一个新的promise用于链式调用
	then(successCallback, failCallback) {
		successCallback = successCallback ? successCallback : value => value;
		failCallback = failCallback ? failCallback : reason => reason;
	
		// failCallback = failCallback ? failCallback : value => value;
		const p = new MyPromise((resolve, reject) => {
			// 对于执行promise executor时候，直接将状态改变的同步操作
			// 就会直接执行对应的回调
			if (this.status === FULFILLED) {
				setTimeout(() => {
					try {
						const value = successCallback(this.value);
						resolvePromise(p, value, resolve, reject);
					} catch (error) {
						reject(error);
					}
				}, 0);
			} else if (this.status === REJECTED) {
				setTimeout(() => {
					try {
						const reason = failCallback(this.reason);
						resolvePromise(p, reason, resolve, reject);
					} catch (e) {
						reject(e);
					}
				}, 0);
			} else {
				this.successCallback.push(() => {
					setTimeout(() => {
						try {
							const value = successCallback(this.value);
							resolvePromise(p, value, resolve, reject);
						} catch (error) {
							reject(error);
						}
					}, 0);
				});

				this.failCallback.push(() => {
					setTimeout(() => {
						try {
							const reason = failCallback(this.reason);
							resolvePromise(p, reason, resolve, reject, 'reject');
						} catch (error) {
							reject(error);
						}
					}, 0);
				});
			}
		});

		return p;
	}

	static all(array) {
		if (!Array.isArray(array)) {
			throw new TypeError("参数必须是一个数组")
		}
		let result = [];
		return new MyPromise((resolve, reject) => {
			let index = 0;
			function addData(key, value) {
				result[key] = value;

				// 使用计数去判断数组中的promise是否已被全部处理
				index++;
				if (index === array.length) {
					resolve(result)
				}
			}

			for (let i = 0, len = array.length; i < len; i++) {

				const value = array[i];
				if (value instanceof MyPromise) {
					value.then(x => {
						addData(i, x)
					}, reason => reject(reason))
				} else {
					addData(i, value)
				}
			}
		})
	}

	static race(array) {
		if (!Array.isArray(array)) {
			throw new TypeError("参数必须是一个数组")
		}

		return new MyPromise((resolve, reject) => {
			for (let i = 0, len = array.length; i < len; i++) {
				const value = array[i];
				if (value instanceof MyPromise) {
					value.then(x => {
						resolve(x)
					}, reason => reject(reason))
				} else {
					resolve(value)
				}
			}
		})
	}

	static allSettled(array) {
		if (!Array.isArray(array)) {
			throw new TypeError("参数必须是一个数组")
		}

		let result = [];
		let index = 0;

		return new MyPromise((resolve, reject) => {
			function addData(key, value) {
				result[key] = value;

				// 使用计数去判断数组中的promise是否已被全部处理
				index++;
				if (index === array.length) {
					resolve(result)
				}
			}

			for (let i = 0, len = array.length; i < len; i++) {
				const value = array[i];
				if (value instanceof MyPromise) {
					value.then(x => {
						addData(i, value.status)
					}, reason => {
						addData(i, value.status)
					})
				} else {
					addData(i, "fulfilled")
				}
			}
		})
	}

	static any(array) {

		return new MyPromise((resolve, reject) => {
			let index = 0;
			for (let i = 0, len = array.length; i < len; i++) {
				const value = array[i];
				if (value instanceof MyPromise) {
					value.then(x => {
						index++
						resolve(x)
					}, () => {
						index++
						if (index === array.length) {
							reject("AggregateError: No Promise in Promise.any was resolved")
						}
					})
				} else {
					index++
					resolve(value)
				}
			}
		})
	}

	static resolve(value) {
		if (value instanceof MyPromise) return value;
		return new MyPromise((resolve, reject) => {
			resolve(value)
		})
	}

	catch(callback) {
		return this.then(undefined, callback)
	}

	finally(callback) {
		return this.then(value => {
			return MyPromise.resolve(callback()).then(() => value)
		}, reason => {
			return MyPromise.resolve(callback()).then(() => { throw reason })
		})
	}

}

function resolvePromise(p, value, resolve, reject, type) {
	if (p === value) {
		reject(new TypeError("循环引用了"));
	}
	if (value instanceof MyPromise) {
		value.then(resolve, reject);
	} else if(type === 'reject') {
		reject(value);
	} else{
		resolve(value)
	}
}




function p1() {
	return new MyPromise((resolve, reject) => {
		setTimeout(() => {
			resolve("p1")
		}, 2000)

	})
}

// p1().then(value => {
// 	log(value)

// 	return 123
// }).then((value) => {
// 	log(value)
// })

function p2() {
	return new MyPromise((resolve, reject) => {
		resolve("p2")
	})
}

function p3() {
	return new MyPromise((resolve, reject) => {
		reject("rejct")
	})
}

// MyPromise.allSettled([p1(), p2()]).then(log)
// MyPromise.all([p1(), p2(), p3()]).catch(log)

// MyPromise.race([p1(), p2()]).then(log)

// MyPromise.any([p1(), p2()]).then().then((value) => {
// 	log(value)
// }).catch((e) => {
// 	log(e)
// }).finally(() => {
// 	log('finally')
// })

MyPromise.any([p3(), p3()]).then((value) => {
	log(value)
}).catch((e) => {
	log(e)
}).finally(() => {
	log('finally')
})

// Mypromise.all(["a", "b", p1(), p2(), "c"]).then((result) => {
// 	log()
// })
